<?php

# -- DÉBUT NOTE DE LICENCE -------------------------------------
#
# Ce fichier fait partie de Clicpage.
#
# Copyright (c) 2010 Francois (www.clicpage.net)
# Distribué selon les termes de la licence GPL version 2.0.
# Consultez le fichier LICENCE ou
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
#
# -- FIN NOTE DE LICENCE ---------------------------------------

class cpModuleUpdateExt extends cpModuleAdmin
{
	
	
	
	
	
	protected $updater;

	
	
	
	
	/// @name Initialisation
	//@{
	

	public function __construct($core)
	{
	
		parent::__construct($core); 
		
		# Permission nécessaire: superadmin
		$this->core->auth->verifierPermission('superadmin', true);	
		
		$this->setTitreModule(__('Erreur'));
		
		$this->setActionsAutorisees('installerTheme', 'installerPlugin');
		
		if($_GET['upd'] == 'plugin' && !empty($_GET['id'])) {
			$this->updExt('plugin', $_GET['id']);
		} elseif($_GET['upd'] == 'theme' && !empty($_GET['id'])) {
			$this->updExt('theme', $_GET['id']);
		}
		
	}
	//@}
    
	
	

	
	/// @name Formulaires
	//@{
	
	
	public function action_installerTheme()
	{
		$this->installerExt('theme');	
	}
	
	
	public function action_installerPlugin()
	{
		$this->installerExt('plugin');
	}
	
	
	public function installerExt($mode)
	{
		
		$module_dir = $mode == 'plugin' ? CP_CHEMIN_PLUGINS : CP_CHEMIN_THEMES;
		
		# Vérifier que le dossier de plugins est accessible en écriture
		if(!is_writable($module_dir)) {
			throw new cpErreur(__('Le dossier n\'est pas accessible en écriture.').' ('.$module_dir.')', null, 401);
		}
		
		# À partir d'une URL
		if(!empty($_POST[$mode.'_url']))
		{
			
			try {
				$url = urldecode($_POST[$mode.'_url']);
				$fichier_zip = $module_dir.'/'.basename($url);
				
				$client = netHttp::initClient($url,$path);
				$client->setTimeout(4);
				$client->setUserAgent($_SERVER['HTTP_USER_AGENT']);
				$client->useGzip(false);
				$client->setPersistReferers(false);
				$client->setOutput($fichier_zip);
				$client->get($path);
				
				if ($client->getStatus() != 200) {
					@unlink($fichier_zip);
					throw new Exception();
				}
			} catch(Exception $e) {
				throw new cpErreur(__('Une erreur est survenue lors du téléchargement de l\'archive.'), null, 501);
			}

			unset($client);
			
		}
		# À partir d'un fichier
		elseif(isset($_FILES[$mode.'_fichier']) && $_FILES[$mode.'_fichier']['error'] != UPLOAD_ERR_NO_FILE)
		{

			try {
				
				files::uploadStatus($_FILES[$mode.'_fichier']);
				
				$basename = files::tidyFileName($_FILES[$mode.'_fichier']['name']);
				
				$filemanager = new filemanager($module_dir);
				$filemanager->uploadFile($_FILES[$mode.'_fichier']['tmp_name'], $basename);
	
				$fichier_zip = $module_dir.'/'.$basename;
				
			} catch (Exception $e) {
				throw new cpErreur(__('Impossible de récupérer le fichier téléversé.'), null, 410);
			}
			
		}
		else {
			throw new cpErreur(__('Vous devez choisir un fichier à téléverser ou inscrire l\'URL d\'installation du module à installer.'), null, 301);
		}
		
		# Vérifications		
		$zip = new cpFileUnzip($fichier_zip);
		$zip->getList(false,'#(^|/)(__MACOSX|\.svn|\.DS_Store|Thumbs\.db)(/|$)#');
		
		$zip_root_dir = $zip->getRootDir();
		
		if($zip_root_dir != false) {
			$module_zip_id = $zip_root_dir;
			$target = $module_dir;
			$dest = $module_dir.'/'.$module_zip_id;
			$description = $zip_root_dir.'/description.php';
			$has_description = $zip->hasFile($description);
		} else {
			$module_zip_id = preg_replace('/\.([^.]+)$/','',basename($fichier_zip));
			$dest = $target = $module_dir.'/'.$module_zip_id;
			$description = 'description.php';
			$has_description = $zip->hasFile($description);
		}
		
		if(!$has_description) {
			$zip->close();
			@unlink($fichier_zip);
			throw new cpErreur(__('Le fichier téléchargé ne semble pas être une archive valide.'), null, 501);
		}
		
		# Dézipper le fichier de description
		$zip->unzip($description, CP_CHEMIN_CACHE.'/_description_temp.php');
		include(CP_CHEMIN_CACHE.'/_description_temp.php');
		@unlink(CP_CHEMIN_CACHE.'/_description_temp.php');
		
		# Vérifier que la version de Clicpage est compatible avec le module
		if(is_array($this->ext_zip_versionCp) && (version_compare($this->ext_zip_versionCp[0], CP_VERSION,'>') || version_compare($this->ext_zip_versionCp[1], CP_VERSION,'<')))
		{
			throw new cpErreur(__('Impossible de poursuivre l\'installation.').' '.
			sprintf(__('Le module %s nécessite une version de Clicpage comprise entre %s et %s pour fonctionner adéquatement.'),
			$module_zip_id, $this->ext_zip_versionCp[0], $this->ext_zip_versionCp[1]), null, 511);
		}
		elseif(!empty($this->ext_zip_versionCp) && version_compare($this->ext_zip_versionCp, CP_VERSION,'>'))
		{
			throw new cpErreur(__('Impossible de poursuivre l\'installation.').' '.
			sprintf(__('Le module %s nécessite Clicpage %s ou ultérieur pour fonctionner adéquatement.'),
			$module_zip_id, $this->ext_zip_versionCp), null, 511);
		}
		
		# Plugin
		if($mode == 'plugin')
		{
			
			# Mise à jour?
			if(is_dir($dest))
			{

				if(version_compare($this->core->plugins[$module_zip_id]['version'], $this->ext_zip_version, '<=') == 1) {
					$zip->close();
					@unlink($fichier_zip);
					throw new cpErreur(__('Impossible de mettre à jour vers une version antérieure.'), null, 510);
				}
				
				# Backup
				try {
					$fichier_backup = CP_CHEMIN_PLUGINS.'/backup-'.$module_zip_id.'-'.$this->core->plugins[$module_zip_id]['version'].'.zip';
					$this->backupModule($module_zip_id, $dest, $fichier_backup);
				} catch (Exception $e) {
					$zip->close();
					@unlink($fichier_zip);
					throw new cpErreur(__('Impossible de créer un fichier de backup car des fichiers ne peuvent pas être lus.'), null, 402);
				}
				
				# Suppression des fichiers actuels
				if(!files::deltree($dest)) {
					$zip->close();
					@unlink($fichier_zip);
					throw new cpErreur(__('Une erreur est survenue lors de la suppression de la version actuelle du module.'), null, 403);
				}
				
			}
			
		}
		# Thème
		else
		{
			
			# Mise à jour?
			if(is_dir($dest))
			{

				$this->core->chargerThemes();
				
				if(version_compare($this->core->themes[$module_zip_id]['version'], $this->ext_zip_version, '>=') == 1) {
					$zip->close();
					@unlink($fichier_zip);
					throw new cpErreur(__('Impossible de mettre à jour vers une version antérieure.'), null, 510);
				}
				
				# Backup
				try {
					$fichier_backup = CP_CHEMIN_THEMES.'/backup-'.$module_zip_id.'-'.$this->core->themes[$module_zip_id]['version'].'.zip';
					$this->backupModule($module_zip_id, $dest, $fichier_backup);
				} catch (Exception $e) {
					$zip->close();
					@unlink($fichier_zip);
					throw new cpErreur(__('Impossible de créer un fichier de backup car des fichiers ne peuvent pas être lus.'), null, 402);
				}
				
				# Suppression des fichiers actuels
				if(!files::deltree($dest)) {
					$zip->close();
					@unlink($fichier_zip);
					throw new cpErreur(__('Une erreur est survenue lors de la suppression de la version actuelle du module.'), null, 403);
				}
				
			}
			
		}
		
		# Installation
		$zip->unzipAll($target);
		$zip->close();
		@unlink($fichier_zip);
		
		# Déplacement du fichier backup
		if(!empty($fichier_backup)) {
			@rename($fichier_backup,$dest.'/'.basename($fichier_backup));
		}
		
		# Redirection
		http::redirect('index.php?module=maintenance&msg=upd'.$mode.'OK&ext_id='.$module_zip_id);
		
	}
	
	
	public function initialiserPlugin($nom, $description, $auteur, $version, $priorite, $versionCp)
	{
		$this->ext_zip_version = $version;
		$this->ext_zip_versionCp = $versionCp;
	}
	
	
	public function initialiserTheme($nom, $description, $auteur, $version, $versionCp)
	{
		$this->ext_zip_version = $version;
		$this->ext_zip_versionCp = $versionCp;
	}
	
	
	public function backupModule($module_id, $module_dossier, $fichier_backup)
	{
		
		$b_fp = @fopen($fichier_backup,'wb');
		if ($b_fp === false) {
			throw new Exception();
		}
		
		$b_zip = new fileZip($b_fp);
		
		$b_zip->addDirectory($module_dossier, $module_id, true);
		
		$b_zip->write();
		fclose($b_fp);
		$b_zip->close();	
		
	}	
	
	
	public function updExt($mode, $identifiant)
	{

		$p_url = 'index.php?module=updateExt&upd='.$mode.'&id='.rawurlencode($identifiant);

		if($mode == 'plugin') {
			$module = $this->core->plugins[$identifiant];
		}
		else {
			$this->core->chargerThemes();
			$module = $this->core->themes[$identifiant];
		}
		
		if(empty($module)) {
			http::redirect('index.php?module=maintenance');
		}

		if($mode == 'plugin') {
			$this->updater = new cpUpdate(CP_PLUGINS_UPDATE_URL.$module['identifiant'].',upd.xml','plugin-'.$module['identifiant'],'stable',CP_CHEMIN_CACHE.'/versions');
		} else {
			$this->updater = new cpUpdate(CP_THEMES_UPDATE_URL.$module['identifiant'].',upd.xml','theme-'.$module['identifiant'],'stable',CP_CHEMIN_CACHE.'/versions');
		}

		$new_v = $this->updater->check($module['version']);
		
		if(!$new_v) {
			http::redirect('index.php?module=maintenance');
		}
		
		# Prépare la destination du zip de mise à jour
		$zip_file = $module['chemin'].'/'.basename($this->updater->getFileURL());
		
		# Étape en cours
		$step = isset($_GET['step']) ? $_GET['step'] : 'check';
		$step = in_array($step,array('check', 'download','backup','unzip')) ? $step : 'check';

		# Mise à jour
		try
		{
			switch ($step)
			{
				case 'check':
					$this->updater->checkIntegrity($module['chemin'].'/digests',$module['chemin'], false);
					http::redirect($p_url.'&step=download');
					break;
				case 'download':
					$this->updater->download($zip_file);
					if (!$this->updater->checkDownload($zip_file)) {
						throw new Exception(
							sprintf(__('L\'archive téléchargée semble être corrompue. '.
							'Essayer de la <a %s>télécharger</a> à nouveau.'),'href="'.$p_url.'&step=download"')
						);
					}
					http::redirect($p_url.'&step=backup');
					break;
				case 'backup':
					$this->updater->backup(
						$zip_file, $module['identifiant'].'/digests',
						$module['chemin'], $module['chemin'].'/digests',
						$module['chemin'].'/backup-'.$module['identifiant'].'-'.$module['version'].'.zip',
						false
					);
					http::redirect($p_url.'&step=unzip');
					break;
				case 'unzip':
					$this->updater->performUpgrade(
						$zip_file, $module['identifiant'].'/digests', $module['identifiant'],
						$module['chemin'], $module['chemin'].'/digests', false
					);
					http::redirect('index.php?module=maintenance&msg=upd'.$mode.'OK&ext_id='.rawurlencode($module['identifiant']));
					break;
			}
		}
		catch (Exception $e)
		{
			$updURL = $mode == 'plugin' ? CP_PLUGINS_UPDATE_URL.$module['identifiant'] : CP_THEMES_UPDATE_URL.$module['identifiant'];
			
			$msg = $e->getMessage();
			
			if ($e->getCode() == cpUpdate::ERR_FILES_CHANGED)
			{
				$msg =
				sprintf(
				__('Comme les fichiers suivants de votre installation ont été modifiés, '.
				'votre installation ne peut être mise à jour. '.
				'Merci de <a href="%s">mettre à jour manuellement</a>.'),
				$updURL);
			}
			elseif ($e->getCode() == cpUpdate::ERR_FILES_UNREADABLE)
			{
				$msg =
				sprintf(__('Les fichiers suivants de votre installation ne peuvent '.
				'pas être lus. Veuillez corriger ceci ou créer un fichier de backup '.
				'nommé %s manuellement.'),
				'<strong>'.$module['chemin'].'/backup-'.$module['identifiant'].'-'.$module['version'].'.zip</strong>');
			}
			elseif ($e->getCode() == cpUpdate::ERR_FILES_UNWRITALBE)
			{
				$msg =
				sprintf(
				__('Les fichiers suivants de votre installation ne peuvent pas être '.
				'écrits. Veuillez corriger ceci ou <a href="%s">mettre à jour manuellement</a>'),
				$updURL);
			}
			
			if (isset($e->bad_files)) {
				$msg .=
				'<ul><li><strong>'.
				implode('</strong></li><li><strong>',$e->bad_files).
				'</strong></li></ul>';
			}
			
			$this->erreurGenerale(new cpErreur($msg));
		}

	}
	//@} 
    
	

	
	
	/// @name Interface du module
	//@{
	
	
	public function interfaceModule()
	{
		
		$html =
			$this->afficherMessages();
		
		if(empty($html)) {
			http::redirect('index.php?module=maintenance');
		}
			
		return $html.
			'<a href="index.php?module=maintenance">'.__('Retour').'</a>';
	
	}
	//@}
	
	
}

?>